package net.alche.fa.core.doing;

// import net.alche.fa.dao.FaSqlDao;
import java.lang.Override;
import net.alche.fa.core.model.Logic;
import net.alche.fa.core.model.FaValue;
import net.alche.fa.biz.iface.FaSqlDao;
import net.alche.fa.core.CodeLevel;
import net.alche.fa.core.SageStone;
import net.alche.fa.core.FaException;
import java.util.List;
import net.alche.fa.core.model.VarItem;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import net.alche.fa.core.model.FaSqlInfo;
// import net.alche.al.util.SpringUtil;
import net.alche.fa.core.FaClass;
import net.alche.fa.core.FaMethod;
import net.alche.fa.core.model.FaParam;
import net.alche.al.util.far.pojo.PageList;
import net.alche.al.util.AC;
// import net.alche.al.common.PageList;
import net.alche.ext.dbUtil.connect.DbExecSql;
// import com.github.pagehelper.Page;
// import com.github.pagehelper.PageHelper;
// import net.alche.al.common.RequestData;
import net.alche.fa.core.FaPlatform;
import java.util.Map.Entry;
import net.alche.fa.core.doing.sub.FillState;
@SuppressWarnings("all") 
// daoDo
public class SqlDo extends AbstractDoing {
  FaSqlDao sqlDao = null;

  @Override
  public boolean matched(String what, Logic g) {
    return "fa-sql".equals(what);
  }

  @Override
  public FaValue doing(Logic g, CodeLevel lvl, SageStone st) throws FaException {
    List<VarItem> sqlList = new ArrayList<> ();
    // Map<String,FaValue> params = new HashMap<> ();
    // for ( String varName: st.getVars().keySet()) {
    //   params.put(varName, st.getVarData(varName, lvl));
    // }
    // FaSqlInfo sqlInfo = new FaSqlInfo();
    // // if ( sqlDao == null) {
    // //   sqlDao = (FaSqlDao) SpringUtil.getBean("faSqlDao");
    // // }
    // lvl.isReturn = true;
    // sqlInfo.setTableName("");
    // sqlInfo.setSpList(sqlList);
    // sqlDao.insertFa(sqlInfo);
    return new FaValue(sqlList);
  }

  public FaValue doDao(FaClass fac, FaMethod method, List<FaValue> params, CodeLevel lvl) throws FaException {
    // if ( sqlDao == null) {
    //   sqlDao = (FaSqlDao) SpringUtil.getBean("faSqlDao");
    // }
    String tableName = fac.getConf().getDef().get("Dao.table");
    // method.getLogicList();
    List<VarItem> sqlList = new ArrayList<> ();
    Map<String,FaValue> paramMap = new HashMap<> ();
    int i = 0;
    for ( FaParam pa: method.getParamList()) {
      paramMap.put(pa.varName, params.get(i++));
    }
    FaSqlInfo sqlInfo = new FaSqlInfo();
    String autoCol = method.getAtTxt("Insert.autoId");
    if ( AC.notEmpty(autoCol)) {
      sqlInfo.setAutoCol(autoCol);
    }
    CodeLevel cl = lvl.instanceDeep();
    try {
      fillSql(sqlList, sqlInfo, paramMap, new Logic(method.getLogicList()), null, cl);
    }
    catch ( FaException ex) {
      throw ex;
    }
    catch ( Exception ex) {
      ex.printStackTrace();
      throw new FaException(ex);
    }
    sqlInfo.setTableName(tableName);
    sqlInfo.setSpList(sqlList);
    String type = method.getDaoType();
    if ( sqlDao == null) {
      // AC.log(AC.json.toStr(sqlList));
      return new FaValue();
    }
    for ( VarItem sp: sqlList) {
      sp.setValue(sp.getValue().replaceAll("" + (char) 160, " "));
    }
    try {
      if ( type.equals("Select")) {
        FaValue selVal = new FaValue();
        selVal.setObjType(method.getReturnType());
        selVal.setType(FaValue.getChar(method.getReturnType()));
        String resultType = this.selectType(method);
        PageList pageList = DbExecSql.popPage();
        // Page page = PageHelper.getLocalPage();
        // if ( pageList != null) {
        //   sqlInfo.setPageList(pageList);
        // }
        // else if ( RequestData.isJdbc || PageHelper.getLocalPage() == null) {
        //   boolean hasLimit = false;
        //   for ( VarItem it: sqlInfo.getSpList()) {
        //     if ( it.getType().equals("s") && it.getValue().equals("limit ")) {
        //       hasLimit = true;
        //       break;
        //     }
        //   }
        //   if ( !hasLimit) {
        //     VarItem vi = new VarItem();
        //     vi.setType("s");
        //     vi.setValue(" limit 0,1000");
        //     sqlInfo.getSpList().add(vi);
        //   }
        // }
        if ( "int".equals(resultType)) {
          Integer intVal = sqlDao.selectInt(sqlInfo);
          if ( intVal != null) {
            selVal.setIntValue(intVal);
            selVal.setNull(false);
          }
          return selVal;
        }
        else if ( "list-int".equals(resultType)) {
          List<Integer> list = sqlDao.selectIntList(sqlInfo);
          FaValue favList = new FaValue(list);
          favList.setObjType("List<Integer>");
          return favList;
        }
        else if ( "list-string".equals(resultType)) {
          List<String> list = sqlDao.selectStringList(sqlInfo);
          FaValue favList = new FaValue(list);
          favList.setObjType("List<String>");
          return favList;
        }
        else {
          List<Map> list = sqlDao.selectList(sqlInfo);
          String pojoName = SqlDo.getPojoName(method.getReturnType());
          if ( list != null && list.size() > 0) {
            if ( this.isBaseNum(pojoName)) {
              return this.returnBase(pojoName, list.get(0));
            }
            // else if "String".equals(pojoName)
            //   return this.returnBase(pojoName, list.get(0))
            else {
              List<FaValue> faList = new ArrayList<> ();
              for ( Map item: list) {
                FaValue val = mapToPojo(item, pojoName, lvl);
                faList.add(val);
              }
              FaValue favList = new FaValue(faList);
              if ( pageList != null) {
                pageList.setList((List) favList.getJava());
              }
              // else if ( page != null && !page.isEmpty()) {
              //   page.clear();
              //   for ( FaValue val: faList) {
              //     page.add(val);
              //   }
              // }
              return favList;
            }
          }
        }
        return selVal;
      }
      else if ( type.equals("SelectOne")) {
        Map map = sqlDao.selectOne(sqlInfo);
        String pojoName = method.getReturnType();
        return mapToPojo(map, pojoName, lvl);
      }
      else if ( type.equals("Update")) {
        int count = sqlDao.updateFa(sqlInfo);
        return new FaValue(count);
      }
      else if ( type.equals("Delete")) {
        int count = sqlDao.deleteFa(sqlInfo);
        return new FaValue(count);
      }
      else {
        int count = sqlDao.insertFa(sqlInfo);
        autoCol(sqlInfo, params, lvl);
        return new FaValue(count);
      }
    }
    catch ( Exception sqlEx) {
      if ( sqlEx instanceof FaException) {
        throw sqlEx;
      }
      else {
        FaException faEx = new FaException(sqlEx);
        faEx.setDebugFall(cl.debugFall);
        throw faEx;
      }
    }
  }

  private String selectType(FaMethod method) {
    String resultType = method.getAtTxt("Select.rt");
    if ( !"".equals(resultType)) {
      return resultType;
    }
    String reType = method.getReturnType();
    if ( reType.equals("int") || reType.equals("Integer")) {
      return "int";
    }
    if ( reType.equals("List<Integer>")) {
      return "list-int";
    }
    if ( reType.equals("List<String>")) {
      return "list-string";
    }
    return resultType;
  }

  private boolean isBaseNum(String pojoName) {
    return FaPlatform.isInt(pojoName) || FaPlatform.isLong(pojoName);
  }

  private FaValue returnBase(String pojoName, Map map) {
    if ( map != null) {
      for ( Object val: map.values()) {
        if ( val != null) {
          return new FaValue(val);
        }
      }
    }
    FaValue fav = new FaValue();
    fav.setObjType(pojoName);
    fav.setType(FaValue.getChar(pojoName));
    return fav;
  }

  private FaValue mapToPojo(Map<String,Object> map, String pojoName, CodeLevel lvl) throws FaException {
    if ( "Map".equals(pojoName)) {
      return new FaValue(map);
    }
    else {
      SageStone st = super.plat.instance(pojoName, lvl).getThisSage();
      FaValue fav = new FaValue(st);
      if ( map != null && !map.isEmpty()) {
        for ( Entry<String,Object> en: map.entrySet()) {
          String attrName = AC.str.getHump(en.getKey());
          if ( st.getVars().get(attrName) == null) {
            continue;
          }
          FaValue varVal = st.getVars().get(attrName);
          st.putVar(attrName, new FaValue(en.getValue(), varVal.type));
        }
      }
      else {
        fav.setNull(true);
      }
      return fav;
    }
  }

  private static void autoCol(FaSqlInfo sqlInfo, List<FaValue> params, CodeLevel lvl) throws FaException {
    if ( sqlInfo.getAutoCol() == null) {
      return;
    }
    if ( sqlInfo.getId() != null && params.size() > 0) {
      FaValue fv = params.get(0);
      if ( fv.getType() == FaValue._SAGE) {
        FaValue vv = fv.getSageValue().getVarData(sqlInfo.getAutoCol(), lvl);
        vv.setNull(false);
        vv.setIntValue(sqlInfo.getId());
      }
    }
  }

  private void fillSql(List<VarItem> sqlList, FaSqlInfo sqlInfo, Map<String,FaValue> params, Logic logic, FillState state, CodeLevel lvl) throws FaException {
    if ( state == null) {
      state = new FillState();
    }
    if ( logic.getObj() != null) {
      String txt = logic.getObj();
      List<String> list = AC.reg.matchers("(#|\\$)\\{[\\w\\.\\[\\]\\ ]+\\}", txt);
      if ( list != null && !list.isEmpty()) {
        for ( String macro: list) {
          int index = txt.indexOf(macro);
          if ( index >= 0) {
            String front = txt.substring(0, index);
            txt = txt.substring(index + macro.length());
            sqlList.add(new VarItem(front, "s"));
            String macroName = macro.substring(2, macro.length() - 1);
            macroName = macroName.trim();
            VarItem vi = new VarItem(macroName, "m");
            if ( macro.startsWith("${")) {
              vi = new VarItem(null, "s");
              Object marcoValue = getJavaValue(params, macroName, lvl);
              if ( marcoValue != null) {
                vi.setValue(marcoValue.toString());
              }
              else {
                vi = null;
              }
            }
            else {
              Object marcoValue = getJavaValue(params, macroName, lvl);
              vi.setTc(marcoValue);
            }
            if ( vi != null) {
              sqlList.add(vi);
            }
          }
        }
      }
      if ( AC.isNotEmpty(txt) && !"\n".equals(txt)) {
        sqlList.add(new VarItem(txt, "s"));
      }
      if ( "set".equals(state.hasTag) && logic.getObj().contains("=")) {
        sqlList.add(new VarItem(" , ", "s"));
      }
    }
    if ( "s-if".equals(logic.getWhat())) {
      doIf(sqlList, sqlInfo, params, logic, state, lvl);
      return;
    }
    if ( "s-foreach".equals(logic.getWhat())) {
      doForeach(sqlList, sqlInfo, params, logic, state, lvl);
      return;
    }
    if ( "s-select-key".equals(logic.getWhat())) {
      Logic gg = logic.getLogicData().getLogicObj();
      String autoCol = gg.getFaDataValue().getStrValue();
      sqlInfo.setAutoCol(autoCol);
    }
    if ( "s-where".equals(logic.getWhat())) {
      if ( "set".equals(state.hasTag)) {
        VarItem vi = sqlList.get(sqlList.size() - 1);
        if ( " , ".equals(vi.getValue())) {
          sqlList.remove(sqlList.size() - 1);
        }
      }
      sqlList.add(new VarItem(" where ", "s"));
      state.hasTag = "where";
    }
    if ( "s-set".equals(logic.getWhat())) {
      sqlList.add(new VarItem(" set ", "s"));
      state.hasTag = "set";
    }
    if ( logic.getChild() != null && !logic.getChild().isEmpty()) {
      for ( Logic cc: logic.getChild()) {
        fillSql(sqlList, sqlInfo, params, cc, state, lvl);
      }
    }
    if ( "s-set".equals(logic.getWhat())) {
      VarItem last = sqlList.get(sqlList.size() - 1);
      if ( last.getValue().equals(" , ")) {
        sqlList.remove(sqlList.size() - 1);
      }
    }
  }

  private void doIf(List<VarItem> sqlList, FaSqlInfo sqlInfo, Map<String,FaValue> params, Logic logic, FillState state, CodeLevel lvl) throws FaException {
    SageStone st = new SageStone(this.plat, null, null);
    st.setCanNull(true);
    Logic gic = new Logic();
    gic.fillLogic(logic);
    gic.setWhat("if");
    try {
      fillStVars(st, params);
    }
    catch ( ClassCastException ex) {
      ex.printStackTrace();
      throw new FaException(ex);
    }
    CodeLevel cl = lvl.instanceDeep();
    FaValue fav = st.runCodeValue(gic, cl);
    if ( fav != null && fav.getBoolValue() && logic.getChild() != null && !logic.getChild().isEmpty()) {
      for ( Logic cc: logic.getChild()) {
        fillSql(sqlList, sqlInfo, params, cc, state, lvl);
      }
    }
  }

  private static void fillStVars(SageStone st, Map<String,FaValue> params) {
    st.fillVars(params);
    st.setCanNull(true);
    if ( params.size() == 1) {
      FaValue val = null;
      String kname = "";
      for ( String key: params.keySet()) {
        val = params.get(key);
        kname = key;
      }
      if ( val != null) {
        if ( val.getType() == FaValue._MAP && !val.isNull()) {
          Map map = (Map)val.getObjValue();
          for ( Object key: map.keySet()) {
            st.putVar(String.valueOf(key), new FaValue(map.get(key)));
          }
        }
        else if ( val.getType() == FaValue._SAGE && !val.isNull()) {
          for ( Entry<String,FaValue> en: val.getSageValue().getVars().entrySet()) {
            st.putVar(en.getKey(), en.getValue());
          }
        }
      }
    }
  }

  private void doForeach(List<VarItem> sqlList, FaSqlInfo sqlInfo, Map<String,FaValue> params, Logic logic, FillState state, CodeLevel lvl) throws FaException {
    Map<String,String> map = getWhatParams(logic);
    String collName = map.get("collection");
    String open = map.get("open");
    String close = map.get("close");
    String itemName = map.get("item");
    String separator = map.get("separator");
    sqlList.add(new VarItem(" ", "s"));
    if ( AC.isNotEmpty(open)) {
      sqlList.add(new VarItem(open, "s"));
    }
    Object marcoValue = null;
    CodeLevel cl = lvl.instanceDeep();
    lvl.debugFall.setLogic(logic);
    try {
      marcoValue = getJavaValue(params, collName, cl);
    }
    catch ( FaException e) {
      if ( e.getDebugFall() == null) {
        e.setDebugFall(lvl.debugFall);
      }
      throw e;
    }
    if ( marcoValue != null && marcoValue instanceof List) {
      List list = (List) marcoValue;
      Map<String,FaValue> pars = new HashMap<> ();
      for ( Entry<String,FaValue> en: params.entrySet()) {
        pars.put(en.getKey(), en.getValue());
      }
      boolean first = true;
      for ( Object obj: list) {
        pars.put(itemName, new FaValue(obj));
        if ( !first && AC.isNotEmpty(separator)) {
          sqlList.add(new VarItem(separator, "s"));
        }
        if ( logic.getChild() != null && !logic.getChild().isEmpty()) {
          for ( Logic child: logic.getChild()) {
            fillSql(sqlList, sqlInfo, pars, child, null, lvl);
          }
        }
        first = false;
      }
    }
    if ( AC.isNotEmpty(close)) {
      sqlList.add(new VarItem(close, "s"));
    }
  }

  public static Map<String,String> getWhatParams(Logic logic) {
    Map<String,String> map = new HashMap<> ();
    for ( Logic item: logic.getParam()) {
      map.put(item.getObj(), item.getData());
    }
    return map;
  }

  private static void fillWhatParams(Map<String,String> map, Logic logic) {
    Logic objLogic = logic.getLogicObj();
    if ( objLogic != null && objLogic.getObj() != null) {
      map.put(objLogic.getObj(), objLogic.getFaDataValue().getStrValue());
    }
    if ( logic.getLogicData() != null) {
      fillWhatParams(map, logic.getLogicData());
    }
  }

  private static Object getJavaValue(Map<String,FaValue> params, String paramName, CodeLevel lvl) throws FaException {
    if ( paramName.contains(".")) {
      String varName = AC.str.pre(paramName, ".");
      FaValue val = params.get(varName);
      if ( val == null) {
        throw new FaException(FaException.NULL_ERROR, "sql[参数]:" + paramName);
      }
      String spName = paramName.substring(varName.length() + 1, paramName.length());
      Object jObj = getDotValue(val, spName, lvl);
      return jObj;
    }
    else {
      if ( params.size() == 1) {
        FaValue val = null;
        String kname = "";
        for ( String key: params.keySet()) {
          val = params.get(key);
          kname = key;
        }
        if ( kname.equals(paramName)) {
          return val.getJava();
        }
        else {
          return getDotValue(val, paramName, lvl);
        }
      }
      else {
        FaValue val = params.get(paramName);
        if ( val == null) {
          throw new FaException(FaException.NULL_ERROR, "sql[参数]:" + paramName);
        }
        // if val != null
        return val.getJava();
      }
    }
  }

  public static Object getDotValue(FaValue obj, String spName, CodeLevel lvl) throws FaException {
    String varName = spName;
    String spName2 = null;
    if ( spName.contains(".")) {
      varName = AC.str.pre(spName, ".");
      spName2 = spName.substring(varName.length() + 1, spName.length());
    }
    Object bak = null;
    // if obj instanceof FaValue
    FaValue fav = (FaValue) obj;
    if ( fav.getType() == FaValue._SAGE || fav.getType() == FaValue._MAP) {
      fav = DotDo.readField(fav, varName, lvl);
      if ( fav != null) {
        bak = fav.getJava();
      }
    }
    else {
      String getName = "get" + (varName.charAt(1) >= 'a' ? AC.str.firstToUpperCase(varName) : varName);
      fav = DotDo.callMethod(null, fav, getName, null, lvl);
      if ( fav != null) {
        bak = fav.getJava();
      }
    }
    // else
    //   String getName = AC.str.firstToUpperCase(varName)
    //   FaValue fav = DotDo.invoke(obj, getName, null, false)
    //   if fav != null
    //     bak = fav.getJava()
    //   
    if ( spName2 != null && bak == null) {
      throw new FaException(FaException.NULL_ERROR, "sql:" + varName);
    }
    return bak;
  }

  public static String getPojoName(String typeName) {
    if ( typeName.contains("<")) {
      typeName = AC.str.sufLast(typeName, "<");
      typeName = AC.str.pre(typeName, ">");
    }
    return typeName;
  }

  public FaSqlDao getSqlDao(){
    return this.sqlDao;
  }
  public void setSqlDao(FaSqlDao sqlDao){
    this.sqlDao = sqlDao;
  }}